package wimon.ui;

import java.awt.*;
import java.awt.event.*;
import java.io.IOException;
import java.net.*;
import java.util.*;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.tree.*;

import org.apache.commons.lang.StringEscapeUtils;

import wimon.net.MeshClient;
import edu.uci.ics.jung.algorithms.layout.*;
import edu.uci.ics.jung.visualization.BasicVisualizationServer;
import edu.uci.ics.jung.visualization.decorators.*;
import edu.uci.ics.jung.visualization.renderers.Renderer.VertexLabel.Position;

@SuppressWarnings("serial")
public class MainPanel extends JPanel {

	static final Color CONSOLE_TEXT_COLOR = Color.WHITE;
	static final Color CONSOLE_BACKGROUND_COLOR = new Color(64, 64, 64);
	static final Font CONSOLE_FONT = new Font("Andale Mono", Font.PLAIN, 14);
	static final Icon ap48Icon = new ImageIcon("images/ap48.png");

	private NetworkInterface[] interfaces;
	private JComboBox interfacesBox;
	private JTree nodesList;
	private JButton discoverButton;
	private JButton executeButton;
	private JTextField executeField;
	private MeshClient client;
	private JTextArea outputTextArea;

	private Action executeAction;
	private Action discoverAction;

	private BasicVisualizationServer<InetAddress, Object> vis;
	private Layout<InetAddress, Object> layout;

	public MainPanel() {
		try {
			client = new MeshClient();
		} catch (IOException e) {
			JOptionPane.showMessageDialog(this, "Error creating UDP socket",
					"Error", JOptionPane.ERROR_MESSAGE);
		}

		this.setupVisualization();
		this.setupActions();
		this.setupUI();
		this.initializeInterfaceList();

	}

	private void setupActions() {
		executeAction = new ExecuteAction();
		executeAction.setEnabled(false);
		discoverAction = new DiscoverAction();
	}

	private JComponent makeTopComponent() {
		interfacesBox = new JComboBox();
		discoverButton = new JButton(discoverAction);
		discoverButton.setFocusPainted(false);
		JPanel topPanel = new JPanel();
		topPanel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
		topPanel.setLayout(new BoxLayout(topPanel, BoxLayout.LINE_AXIS));
		topPanel.add(interfacesBox);
		topPanel.add(Box.createHorizontalStrut(5));
		topPanel.add(discoverButton);
		topPanel.add(Box.createHorizontalGlue());
		return topPanel;
	}

	private JComponent makeLeftComponent() {
		JPanel p = new JPanel();
		p.setLayout(new BoxLayout(p, BoxLayout.PAGE_AXIS));
		DefaultMutableTreeNode root = new DefaultMutableTreeNode(client
				.getAddress());
		nodesList = new JTree(root);
		nodesList.setOpaque(false);
		DefaultTreeCellRenderer cr = (DefaultTreeCellRenderer) nodesList
				.getCellRenderer();
		cr.setLeafIcon(new ImageIcon("images/ap16-2.png"));
		cr.setBackgroundNonSelectionColor(new Color(0, 0, 0, 0));
		nodesList.setRootVisible(false);
		nodesList.getSelectionModel().setSelectionMode(
				TreeSelectionModel.SINGLE_TREE_SELECTION);
		nodesList.addTreeSelectionListener(new TreeSelectionListener() {

			@Override
			public void valueChanged(TreeSelectionEvent e) {
				String ip = e.getPath().getPath()[1].toString();
				try {
					client.setNodeAddress(InetAddress.getByName(ip));
					executeAction.setEnabled(true);
				} catch (UnknownHostException e1) {
					e1.printStackTrace();
				}
			}

		});
		JScrollPane nodesScrollPane = new JScrollPane(nodesList);
		nodesScrollPane.setPreferredSize(new Dimension(180, 0));
		nodesScrollPane
				.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
		nodesScrollPane.setBorder(BorderFactory.createTitledBorder("Nodes"));
		p.add(nodesScrollPane);
		return p;
	}

	private JComponent makeContentComponent() {
		outputTextArea = new JTextArea();
		outputTextArea.setEditable(false);
		outputTextArea.setLineWrap(false);
		outputTextArea.setAutoscrolls(true);
		outputTextArea.setFont(CONSOLE_FONT);
		// outputTextArea.setOpaque(false);
		outputTextArea.setBackground(CONSOLE_BACKGROUND_COLOR);
		outputTextArea.setForeground(CONSOLE_TEXT_COLOR);
		outputTextArea.setMargin(new Insets(5, 5, 5, 5));
		JScrollPane outputScrollPane = new JScrollPane(outputTextArea);
		outputScrollPane.setPreferredSize(new Dimension(500, 300));
		outputScrollPane.setAutoscrolls(true);

		JTabbedPane tabbedPane = new JTabbedPane();
		tabbedPane.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
		tabbedPane.addTab("Console", outputScrollPane);
		tabbedPane.addTab("Toplogy", vis);
		return tabbedPane;
	}

	private JComponent makeRightComponent() {
		executeField = new JTextField(14);
		executeField.getKeymap().addActionForKeyStroke(
				KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), executeAction);
		executeButton = new JButton(executeAction);

		JPanel rightPanel = new JPanel();
		// rightPanel.setOpaque(false);
		GridBagLayout rightPanelLayout = new GridBagLayout();
		rightPanel.setLayout(rightPanelLayout);
		rightPanel.setBorder(BorderFactory.createTitledBorder("Control"));
		GridBagConstraints c = new GridBagConstraints();

		c.fill = GridBagConstraints.HORIZONTAL;
		c.anchor = GridBagConstraints.FIRST_LINE_START;
		c.weightx = 1;
		rightPanel.add(executeField, c);

		c.fill = GridBagConstraints.NONE;
		c.anchor = GridBagConstraints.FIRST_LINE_END;
		c.weightx = 0;
		c.gridy = 1;
		rightPanel.add(executeButton, c);

		c.weighty = 1;
		c.gridy = 2;
		rightPanel.add(Box.createGlue(), c);

		return rightPanel;
	}

	private void setupVisualization() {
		layout = new FRLayout<InetAddress, Object>(client.getGraph());
		layout.setSize(new Dimension(400, 350));
		vis = new BasicVisualizationServer<InetAddress, Object>(layout);
		vis.setPreferredSize(new Dimension(400, 350));
		vis.getRenderContext().setVertexLabelTransformer(
				new ToStringLabeller<InetAddress>());
		vis.getRenderContext().setVertexIconTransformer(
				new DefaultVertexIconTransformer<InetAddress>() {
					@Override
					public Icon transform(InetAddress v) {
						return ap48Icon;
					}
				});
		vis.getRenderer().getVertexLabelRenderer().setPosition(Position.S);
	}

	private void setupUI() {
		JComponent p;

		this.setLayout(new BorderLayout());
		p = this.makeTopComponent();
		this.add(p, BorderLayout.NORTH);

		p = this.makeLeftComponent();
		this.add(p, BorderLayout.WEST);

		p = this.makeContentComponent();
		this.add(p, BorderLayout.CENTER);

		p = this.makeRightComponent();
		this.add(p, BorderLayout.EAST);
	}

	private void initializeInterfaceList() {
		try {
			interfaces = MeshClient.getInterfaces();
			for (NetworkInterface ni : interfaces) {
				Enumeration<InetAddress> ias = ni.getInetAddresses();

				String s = ni.getDisplayName() + " (" + ni.getName() + ") - ";
				while (ias.hasMoreElements())
					s += ias.nextElement().getHostAddress();

				interfacesBox.addItem(s);
			}
		} catch (Exception e) {
			JOptionPane.showMessageDialog(this,
					"Error enumerating Network Interfaces:\n" + e.getMessage(),
					"Error", JOptionPane.ERROR_MESSAGE);
		}
	}

	class DiscoverAction extends AbstractAction {

		public DiscoverAction() {
			super("Discover");
		}

		@Override
		public void actionPerformed(ActionEvent arg0) {
			try {
				executeAction.setEnabled(false);

				DefaultTreeModel m = (DefaultTreeModel) nodesList.getModel();
				DefaultMutableTreeNode root = (DefaultMutableTreeNode) m
						.getRoot();
				root.removeAllChildren();

				while (root.getChildCount() > 0)
					m
							.removeNodeFromParent((MutableTreeNode) root
									.getChildAt(0));

				client.discover();
				InetAddress[] ias = client.getDiscoveredAddresses();

				for (InetAddress ia : ias) {
					DefaultMutableTreeNode n = new DefaultMutableTreeNode(ia
							.getHostAddress());
					m.insertNodeInto(n, root, root.getChildCount());
				}
				m.reload();
				vis.repaint();
			} catch (Exception e) {
				e.printStackTrace();
				JOptionPane.showMessageDialog(MainPanel.this,
						"Error discovering nodes:\n" + e.getMessage());
			}
		}

	}

	class ExecuteAction extends AbstractAction {

		public ExecuteAction() {
			super("Execute");
		}

		@Override
		public void actionPerformed(ActionEvent arg0) {
			try {
				String c = executeField.getText();
				Date t = Calendar.getInstance().getTime();
				outputTextArea.append(String.format(
						"[%1$tH:%1$tM:%1$tS %1$tL] executing '%2$s'%n", t, c));

				executeField.selectAll();
				String s = StringEscapeUtils.escapeJava(c);
				MeshClient.Message msg = client.execute(s);

				outputTextArea.append(msg.getBodyAsString() + "\n");
			} catch (Exception e) {
				e.printStackTrace();
				JOptionPane.showMessageDialog(MainPanel.this,
						"Error executing:\n" + e.getMessage());
			}
		}

	}

}
